/*******************************************************************************************

	R&Q plugin utils
	
	  for less headache formed as a header ;-)

	by OverQuantum   17.05.2006 - 23.11.2006

	(c) 2006-2007 OverQuantum

*******************************************************************************************/

//Getters for DWORD and WORD in BigEndian and LittleEndian
DWORD Get_DWORD_BE(BYTE * bFrom)
{
	DWORD rv;
	rv = (bFrom[0]<<24)|(bFrom[1]<<16)|(bFrom[2]<<8)|(bFrom[3]);
	return rv;
}
DWORD Get_DWORD_LE(BYTE * bFrom)
{
	DWORD rv;
	memcpy(&rv,bFrom,4);
	return rv;
}
WORD Get_WORD_BE(BYTE * bFrom)
{
	WORD rv;
	rv = (bFrom[0]<<8)|(bFrom[1]);
	return rv;
}
WORD Get_WORD_LE(BYTE * bFrom)
{
	WORD rv;
	memcpy(&rv,bFrom,2);
	return rv;
}


//Putters for DWORD and WORD in BigEndian and LittleEndian
void Put_DWORD_LE(BYTE * bTo, DWORD iVal)
{
	memcpy(bTo,&iVal,4);
	return;
}
void Put_DWORD_BE(BYTE * bTo, DWORD iVal)
{
	bTo[0]=(BYTE)((iVal>>24)&0xFF);
	bTo[1]=(BYTE)((iVal>>16)&0xFF);
	bTo[2]=(BYTE)((iVal>>8)&0xFF);
	bTo[3]=(BYTE)(iVal&0xFF);
	return;
}
void Put_WORD_LE(BYTE * bTo, WORD iVal)
{
	memcpy(bTo,&iVal,4);
	return;
}
void Put_WORD_BE(BYTE * bTo, WORD iVal)
{
	bTo[0]=(BYTE)((iVal>>8)&0xFF);
	bTo[1]=(BYTE)(iVal&0xFF);
	return;
}

//Getters of some rejetto's data types

DWORD RQ_GetINTLIST(BYTE * bArr, DWORD * iPos, DWORD iLen, DWORD * iListLen, int ** pList)
{
	DWORD iLLen,i;
	if ((*iPos+4)>iLen) return 1;
	memcpy(&iLLen,bArr+*iPos,4);
	*iPos+=4;
	i = (iLen-*iPos)/4;
    
	if (i>=iLLen) iLLen = i;
	*pList = (int*)(bArr+*iPos);
	*iPos += iLLen*4;
	*iListLen = iLLen;
	return 0;
}


DWORD RQ_GetPOINTER(BYTE * bArr, DWORD * iPos, DWORD iLen, DWORD * pPointer)
{
	if ((*iPos+4)>iLen) return 1;
	memcpy(pPointer,bArr+*iPos,4);
    *iPos+=4;
	return 0;
}

DWORD RQ_GetINT(BYTE * bArr, DWORD * iPos, DWORD iLen, int * pINT)
{
	if ((*iPos+4)>iLen) return 1;
	memcpy(pINT,bArr+*iPos,4);
    *iPos+=4;
	return 0;
}

DWORD RQ_GetDOUBLE(BYTE * bArr, DWORD * iPos, DWORD iLen, double * pDOUBLE)
{
	if ((*iPos+8)>iLen) return 1;
	memcpy(pDOUBLE,bArr+*iPos,8);
    *iPos+=8;
	return 0;
}

DWORD RQ_GetISTRING(BYTE * bArr, DWORD * iPos, DWORD iLen, char * pString, DWORD iStrMax)
{
	DWORD iStrLen,i;
	if ((*iPos+4)>iLen) return 1;
	memcpy(&iStrLen,bArr+*iPos,4);
	*iPos+=4;
	i = (iLen-*iPos);
	if (iStrLen>i) iStrLen = i;
	
	i = iStrLen;
	iStrMax--;//1 for NULL-terminator
	if (i>iStrMax) i = iStrMax;
	
	strncpy(pString,(char*)(bArr+*iPos),i);
    pString[i]=0; //NULL-terminator

    *iPos+=iStrLen;
	return 0;
}

DWORD RQ_GetISTRING_ptr(BYTE * bArr, DWORD * iPos, DWORD iLen, char ** pString, DWORD * iStrLen)
{
	DWORD iStrLen2,i;
	if ((*iPos+4)>iLen) return 1;
	memcpy(&iStrLen2,bArr+*iPos,4);
	*iPos+=4;
	i = (iLen-*iPos);
	if (iStrLen2>i) iStrLen2 = i;
	
	*pString = (char*)(bArr+*iPos);
	*iStrLen = iStrLen2;
    *iPos+=iStrLen2;
	return 0;
}


//Get ASCII string with byte-len prefix (aka L1-string)

DWORD RQ_GetShortString(BYTE * bArr, DWORD * iPos, DWORD iLen, char * pString, DWORD iStrMax)
{
	DWORD iStrLen,i;
	if ((*iPos+1)>iLen) return 1;
	iStrLen = bArr[*iPos];
	(*iPos)++;
	i = (iLen-*iPos);
	if (iStrLen>i) iStrLen = i;
	
	i = iStrLen;
	iStrMax--;//1 for NULL-terminator
	if (i>iStrMax) i = iStrMax;
	
	strncpy(pString,(char*)(bArr+*iPos),i);
    pString[i]=0; //NULL-terminator

    *iPos+=iStrLen;
	return 0;
}

//Decode FLAP header (6 bytes)
DWORD RQ_DecodeFLAP(BYTE * bArr, DWORD * iPos, DWORD iLen, BYTE * bChannel, WORD * iSeq, WORD * iSize)
{
	if ((*iPos+6)>iLen) return 1;
	if (bArr[*iPos]!=0x2A) return 2;
	(*iPos)++;
	*bChannel = bArr[*iPos];(*iPos)++;
	*iSeq = Get_WORD_BE(bArr+*iPos);(*iPos)+=2;
	*iSize = Get_WORD_BE(bArr+*iPos);(*iPos)+=2;
	return 0;
}

//Decode SNAC header (10 bytes)
DWORD RQ_DecodeSNAC(BYTE * bArr, DWORD * iPos, DWORD iLen, WORD * iS1, WORD * iS2, WORD * iFlags, DWORD * iReqID)
{
	if ((*iPos+10)>iLen) return 1;
	*iS1 = Get_WORD_BE(bArr+*iPos);(*iPos)+=2;
	*iS2 = Get_WORD_BE(bArr+*iPos);(*iPos)+=2;
	*iFlags = Get_WORD_BE(bArr+*iPos);(*iPos)+=2;
	*iReqID = Get_DWORD_BE(bArr+*iPos);(*iPos)+=4;
	return 0;
}

//Construct FLAP-SNAC header (16 bytes)
DWORD RQ_SnacPack(BYTE * bArr, DWORD iPos, BYTE iChannel, WORD iSNAC1, WORD iSNAC2, WORD Flags, DWORD iReqID)
{
	bArr[iPos]=0x2A;iPos++; //FLAP id byte
	bArr[iPos]=iChannel;iPos++;//FLAP channel
	Put_WORD_BE(bArr+iPos,0x4000);iPos+=2;//FLAP datagram seq number
	Put_WORD_BE(bArr+iPos,0);iPos+=2;//FLAP data size
	Put_WORD_BE(bArr+iPos,iSNAC1);iPos+=2; //SNAC family 
	Put_WORD_BE(bArr+iPos,iSNAC2);iPos+=2;//SNAC subtype
	Put_WORD_BE(bArr+iPos,Flags);iPos+=2;//SNAC flags
	Put_DWORD_BE(bArr+iPos,iReqID);iPos+=4;//SNAC request id
	return 0;
}

//Putters of some rejetto's data types

DWORD RQ_PutISTRING(BYTE * bArr, DWORD * iPos, char * cString)
{
    DWORD iLen;    
	iLen = (DWORD)strlen(cString);
	memcpy(bArr+*iPos,&iLen,4);
	*iPos+=4;
	memcpy(bArr+*iPos,cString,iLen);
	*iPos+=iLen;
	return 0;
}

DWORD RQ_PutINT(BYTE * bArr, DWORD * iPos, int iINT)
{
	memcpy(bArr+*iPos,&iINT,4);
	*iPos+=4;
	return 0;
}

//Put ASCII string with byte-len prefix

DWORD RQ_PutShortString(BYTE * bAnswer, DWORD * iPos, char * sFromUser)
{
	DWORD iLen=(DWORD)strlen(sFromUser);
	if (iLen>255) return 1;
	bAnswer[*iPos]=(BYTE)iLen;(*iPos)++;
	memcpy(bAnswer+*iPos,sFromUser,iLen);(*iPos)+=iLen;
	return 0;
}

//Constructor of ICQ offline message
// sMessage - text
// iToUIN   - destination UIN
// iFromUIN - sender UIN

DWORD RQ_ConstuctOfflineMsg(BYTE * bArr, DWORD * iPos, char * sMessage, DWORD iToUIN, DWORD iFromUIN)
{
	SYSTEMTIME st1;
	DWORD iMsgLen,iBegPack;

	iMsgLen=(DWORD)strlen(sMessage)+1;
	if ((iMsgLen+38)>=0x10000) return 1; //otherwise overflow SNAC len

	GetSystemTime(&st1);

	iBegPack=*iPos;
	RQ_SnacPack(bArr,*iPos,2,0x15,0x3,1,3);(*iPos)+=16;  //SNAC(15,03)
	Put_WORD_BE(bArr+*iPos,1);(*iPos)+=2;//TLV.Type(1) - encapsulated META_DATA
	Put_WORD_BE(bArr+*iPos,(WORD)(iMsgLen+24));(*iPos)+=2;//TLV.Length
	Put_WORD_LE(bArr+*iPos,(WORD)(iMsgLen+22));(*iPos)+=2;//data chunk size (TLV.Length-2)
	Put_DWORD_LE(bArr+*iPos,iToUIN);(*iPos)+=4;//msg target uin
	Put_WORD_LE(bArr+*iPos,0x41);(*iPos)+=2;//data type - offline message
	Put_WORD_LE(bArr+*iPos,0);(*iPos)+=2;//request sequence number
	Put_DWORD_LE(bArr+*iPos,iFromUIN);(*iPos)+=4;//message sender uin
	Put_WORD_LE(bArr+*iPos,st1.wYear);(*iPos)+=2;//year when message was sent
	bArr[*iPos]=(BYTE)st1.wMonth;(*iPos)++;//month when message was sent
	bArr[*iPos]=(BYTE)st1.wDay;(*iPos)++;//day when message was sent
	bArr[*iPos]=(BYTE)st1.wHour;(*iPos)++;//hour (GMT) when message was sent
	bArr[*iPos]=(BYTE)st1.wMinute;(*iPos)++;//minute when message was sent
	bArr[*iPos]=1;(*iPos)++;//   message type  -   MTYPE_PLAIN  
	bArr[*iPos]=0;(*iPos)++;//   message flags 
	Put_WORD_LE(bArr+*iPos,(WORD)iMsgLen);(*iPos)+=2;//message string length
	memcpy(bArr+*iPos,sMessage,iMsgLen);(*iPos)+=iMsgLen;//message string (asciiz)
	
	Put_WORD_BE(bArr+iBegPack+4,(WORD)((*iPos)-iBegPack-6));//Fix SNAC len
	return 0;
}

//Constructor of server info message  (like from UIN=1, with message type = MTYPE_SERVER)
// sMessage - text

DWORD RQ_ConstuctServerMsg(BYTE * bArr, DWORD * iPos, char * sMessage)
{
	DWORD iBegPack=*iPos;
	DWORD iMsgLen=(DWORD)strlen(sMessage)+1;
	if ((iMsgLen+40)>=0x10000) return 1; //otherwise overflow SNAC len
	RQ_SnacPack(bArr,*iPos,2,0x04,0x7,0,0);(*iPos)+=16;  //SNAC(4,7)
	memset(bArr+*iPos,0,8);(*iPos)+=8;//msg-id cookie
	Put_WORD_BE(bArr+*iPos,4);(*iPos)+=2;//message channel  - typed old-style messages
	bArr[*iPos]=1;(*iPos)++;//screenname string length
	bArr[*iPos]=0x31;(*iPos)++;//screenname string
	Put_WORD_BE(bArr+*iPos,0);(*iPos)+=2;//sender warning level
	Put_WORD_BE(bArr+*iPos,0);(*iPos)+=2;//number of TLVs in fixed part = 0
	Put_WORD_BE(bArr+*iPos,5);(*iPos)+=2;//TLV.Type(0x05) - message data
	Put_WORD_BE(bArr+*iPos,(WORD)(iMsgLen+10));(*iPos)+=2;//TLV.Length
	Put_DWORD_LE(bArr+*iPos,1);(*iPos)+=4;//Sender uin
	bArr[*iPos]=9;(*iPos)++;//   Message type  9 = MTYPE_SERVER     Message from OSCAR server (0xFE formatted) 
	bArr[*iPos]=0;(*iPos)++;//Message flags
	Put_WORD_LE(bArr+*iPos,(WORD)(iMsgLen));(*iPos)+=2;//Message string length
	memcpy(bArr+*iPos,sMessage,iMsgLen);(*iPos)+=iMsgLen;//Message string
	Put_WORD_LE(bArr+*iPos,0);(*iPos)+=2;//terminator
	
	Put_WORD_BE(bArr+iBegPack+4,(WORD)((*iPos)-iBegPack-6));//Fix SNAC len
	return 0;
}

//Constructor of old-style message
// sMessage - text
DWORD RQ_ConstuctOldStyleMsg(BYTE * bArr, DWORD * iPos, char * sMessage, DWORD iFromUIN)
{
	DWORD iBegPack=*iPos;
	DWORD iMsgLen=(DWORD)strlen(sMessage)+1;
	DWORD i;
	char sUIN[15];

	if (iMsgLen>0xFF00) return 1; //(appox.) otherwise overflow SNAC len

	i = sprintf(sUIN,"%i",iFromUIN);
	RQ_SnacPack(bArr,*iPos,2,0x04,0x7,0,0);(*iPos)+=16;  //SNAC(4,7)
	memset(bArr+*iPos,0,8);(*iPos)+=8;//msg-id cookie
	Put_WORD_BE(bArr+*iPos,4);(*iPos)+=2;//message channel  - typed old-style messages
	bArr[*iPos]=(BYTE)i;(*iPos)++;//screenname string length
	memcpy(bArr+*iPos,sUIN,i);(*iPos)+=i;//screenname string
	Put_WORD_BE(bArr+*iPos,0);(*iPos)+=2;//sender warning level
	Put_WORD_BE(bArr+*iPos,0);(*iPos)+=2;//number of TLVs in fixed part = 0
	Put_WORD_BE(bArr+*iPos,5);(*iPos)+=2;//TLV.Type(0x05) - message data
	Put_WORD_BE(bArr+*iPos,(WORD)(iMsgLen+10));(*iPos)+=2;//TLV.Length
	Put_DWORD_LE(bArr+*iPos,iFromUIN);(*iPos)+=4;//Sender uin
	bArr[*iPos]=1;(*iPos)++;//   Message type  1 =   MTYPE_PLAIN  
	bArr[*iPos]=0;(*iPos)++;//Message flags
	Put_WORD_LE(bArr+*iPos,(WORD)(iMsgLen));(*iPos)+=2;//Message string length
	memcpy(bArr+*iPos,sMessage,iMsgLen);(*iPos)+=iMsgLen;//Message string
	Put_WORD_LE(bArr+*iPos,0);(*iPos)+=2;//terminator
	
	Put_WORD_BE(bArr+iBegPack+4,(WORD)((*iPos)-iBegPack-6));//Fix SNAC len
	return 0;
}



//Constructor of ICQ Auto-away message
// NOT WORK WITH R&Q
DWORD RQ_ConstuctAutoMsg(BYTE * bArr, DWORD * iPos, char * sMessage, char * sFromUser, DWORD iReqID)
{
	DWORD iMsgLen=(DWORD)strlen(sMessage)+1;
	DWORD iBegPack=*iPos;

	if (iMsgLen>0xFF00) return 1; //(appox.) otherwise overflow SNAC len

	RQ_SnacPack(bArr,*iPos,2,0x4,0xB,0,iReqID);(*iPos)+=16;
	memset(bArr+*iPos,0,8);(*iPos)+=8;//msg-id cookie
	Put_WORD_BE(bArr+*iPos,2);(*iPos)+=2;//message channel
	if (RQ_PutShortString(bArr,iPos,sFromUser)) return 2;
	Put_WORD_BE(bArr+*iPos,3);(*iPos)+=2;//reason code (2 - busted payload, 3 - channel specific)

	Put_WORD_LE(bArr+*iPos,0x1B);(*iPos)+=2;//length of following data
	Put_WORD_LE(bArr+*iPos,9);(*iPos)+=2;//protocol version
	memset(bArr+*iPos,0,16);(*iPos)+=16;//guid - plugin or zero bytes
	Put_WORD_LE(bArr+*iPos,0);(*iPos)+=2;//unknown
	Put_DWORD_LE(bArr+*iPos,3);(*iPos)+=4;//client capabilities flags
	bArr[*iPos]=0;(*iPos)++;//unknown
	Put_WORD_LE(bArr+*iPos,0xFFFF);(*iPos)+=2;//unknown

	Put_WORD_LE(bArr+*iPos,0x0E);(*iPos)+=2;//length of following data
	Put_WORD_LE(bArr+*iPos,0xFFFF);(*iPos)+=2;//unknown
	memset(bArr+*iPos,0,12);(*iPos)+=12;//unknown

	bArr[*iPos]=0xE8;(*iPos)++;//message type   -  MTYPE_AUTOAWAY  
	bArr[*iPos]=0x03;(*iPos)++;//message flags  -  MFLAG_AUTO  
	Put_WORD_LE(bArr+*iPos,4);(*iPos)+=2;//status code
	Put_WORD_LE(bArr+*iPos,0);(*iPos)+=2;//priority code
	Put_WORD_LE(bArr+*iPos,(WORD)iMsgLen);(*iPos)+=2;
	memcpy(bArr+*iPos,sMessage,iMsgLen);(*iPos)+=iMsgLen;//message
	
	Put_WORD_BE(bArr+iBegPack+4,(WORD)((*iPos)-iBegPack-6));//Fix SNAC len
	return 0;
}

//Print INTLIST into file
DWORD  RQ_fprintfINTLIST(FILE * ffFile, BYTE * bArray, DWORD iLen)
{
	DWORD iRv,i,j,iListLen;
	DWORD * pINT;
	if (iLen<4)
	{
		fprintf(ffFile," [wrong list]");
		return 0;
	}
	pINT = (DWORD*)bArray;

	iListLen = *pINT;
	iRv = 4;

	fprintf(ffFile,"{");
	pINT++;
	
	//test, that list is not larger, than rest of array
	i = (iLen-4)/4;
	if (iListLen>iLen)
	{
		iListLen = i;
		fprintf(ffFile,"[only %i elements]",iListLen);
	}
	iRv+=iListLen*4;
	for (i=0;i<iListLen;i++)
	{
		j = *pINT;
		fprintf(ffFile,"%i,",j);
		pINT++;
	}
	fprintf(ffFile,"}");

	return iRv;
}

//Convert VariantDate (VT_DATE) into SYSTEMTIME
DWORD RQ_TIME2SystemTime(double variantdate, SYSTEMTIME * st1)
{
	__int64 i64;
	i64 = (__int64)(864000000000 * variantdate); // (1 day = 86400 seconds) multiply (VT_DATE value (that is number of days))
	i64 += 94353120000005000; //difference between January 1, 1601 (basis of FILETIME) and December 30, 1899 (basis of VT_DATE)
	                          //PLUS  0.5 ms for rounding wMilliseconds to nearest
	if (FileTimeToSystemTime((FILETIME*)&i64,st1)==0) return 1; //If the FileTimeToSystemTime fails, the return value is zero.
	return 0;
}